需要解决的问题

在编写Python代码的过程中,为了更好的管理代码,我们会使用到包(package)、模块(module)、类(class)、函数(function)。
介绍一下这几个概念:

  • 首先是模块,module可以简单理解为就是一个.py文件,这个文件里面可以包含类、变量、常量和函数等;
  • 然后是包,简单理解含有__init__.py的目录就算是一个包,需要明确的是在python中包其实是一种特殊的模块,但模块并不是包。原因其实很简单,sys.modules我们获取到的结果中包含包和模块,而module.__package__则会获取到包的信息;
  • 最后是类(函数在这里就不说了),类会写在.py文件中,一般为了控制文件的大小,通常将一个或几个有关联的类写在一个文件中。当然个人还是觉得一个类独占一个文件比较好,除非你想看见一大堆乱糟糟的代码挤在一个超大的文件里。

然后再来看看我们日常使用包、模块和类的做法

# 目录结构
#|--demo.py
#|--package_a
#   |--__init__.py
#   |--class_a.py  类ClassA
#   |--class_b.py  类ClassB

# ./demo.py

# 导入方式一
from package_a.class_a import ClassA
from package_a.class_a import ClassB
a = ClassA()
b = ClassB()

# 导入方式二
import package_a.class_a as ca
import package_a.class_b as cb
a = ca.ClassA()
b = cb.ClassB()

好的,我们的问题来了,怎么样才能减少我们导入部分的代码呢?或者说怎么样才能使得一些相关联的模块不需要一个一个的进行导入呢?最终实现到下面的样子是不是会更好

# ./demo.py

from package_a import *
a = ClassA()
b = ClassB()

解决的方法

# ./common.py
import sys

_packet_ = {}
# 装饰器,func是类或者函数
def export(func):
    module = sys.modules[func.__module__]        # 获取func的模块对象
    package = sys.modules[module.__package__]    # 由模块对象得到包对象
    package.__dict__[func.__name__] = func       # 把func添加到包的__dict__里
    # 生成所有使用该解决方案的包的__all__变量,并把导出的func添加进去
    if not package.__name__ in _packet_:
        _packet_[package.__name__] = []
    _packet_[package.__name__].append(func.__name__)
    # 原封不动地把func返回
    return func

# 在包的__init__.py里用于获取__all__
def packet(name):
    if not name in _packet_:
        _packet_[name] = []
    return _packet_[name]

实际使用

# 目录结构
#|--common.py(就是上面的装饰器文件)
#|--demo.py
#|--package_a
#   |--__init__.py
#   |--class_a.py  类ClassA
#   |--class_b.py  类ClassB

首先在模块中处理我们创建的类

# ./package_a/class_a.py

import common

# 使用export装饰器,装饰要导出的类或函数
@common.export
class ClassB(object):
    def __init__(self):
        print 'This is ClassB.'

然后在包的__init__.py中修改包自身

# ./package_a/__init__.py

import common

# 注意,这步很重要!!!导入将要导出的子模块,需要具体模块名字,此处import * 不可用
from . import class_a, class_b

# 用packet初始化包的__all__,主要是用于支持 "from . import *" 导入
__all__ = common.packet(__name__)

# 因为用__all__会影响"from . import *"。所以用export把__init__.py里的成员,加入__all__
@common.export
def pafunc():
    """ 这是一个包内部的函数 """
    print('pafunc')

最终就可以实现我们前面所期望的导入方式了。

# ./demo.py

from package_a import *
a = ClassA()

qiaojun_peng
182 声望2 粉丝

最有用的语言是 English,其次可能是 Python